﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Services.Description;
using System.Xml;

namespace SearchingWebServices
{
    public static class WsdlParser
    {

        public static List<ServiceDefinition> Parse(string wsdlUrl)
        {
            ServiceDescription serviceDescription;
            using (var reader = new XmlTextReader(wsdlUrl))
            {
                serviceDescription = ServiceDescription.Read(reader);
            }

            var sdCol = new ServiceDescriptionCollection { serviceDescription };
            var serviceList = new List<ServiceDefinition>();

            // Get services (likely only one...but just in case...)
            foreach (Service service in serviceDescription.Services)
            {
                var def = new ServiceDefinition { Name = service.Name, Description = service.Documentation };
                serviceList.Add(def);

                foreach (Port port in service.Ports)
                {
                    var bind = serviceDescription.Bindings[port.Binding.Name];
                    var portTyp = serviceDescription.PortTypes[bind.Type.Name];

                    // Get each operation
                    foreach (Operation op in portTyp.Operations)
                    {
                        var sop = new ServiceOperation { Name = op.Name, Description = op.Documentation };
                        def.Operations.Add(sop);

                        // Get each message (input/output)
                        foreach (OperationMessage opMsg in op.Messages)
                        {
                            var msg = sdCol.GetMessage(opMsg.Message);
                            var oio = new OpInOut { Name = msg.Name };

                            if (opMsg is OperationInput)
                            {
                                sop.Input = oio;
                            }
                            else if (opMsg is OperationOutput)
                            {
                                sop.Output = oio;
                            }
                            else
                            {
                                Console.WriteLine("\t\tFault Message: {0}", opMsg.Message);
                            }

                            // Get each part (parameter)
                            foreach (MessagePart part in msg.Parts)
                            {
                                oio.Parameters.Add(new InOutParam
                                                   {
                                                       Name = part.Name,
                                                       Type = part.Element == XmlQualifiedName.Empty
                                                                  ? part.Type.Name
                                                                  : part.Element.Name
                                                   });
                            }
                        }
                    }
                }
            }
            return serviceList;
        }

        public static List<string> TokenizeServiceInfo(IEnumerable<ServiceDefinition> services)
        {
            var wordSet = new HashSet<string>();

            foreach (var service in services)
            {
                foreach (var word in Tokenizer.Tokenize(service.Name).Where(word => !wordSet.Contains(word)))
                {
                    wordSet.Add(Tokenizer.Lemmatizer.Lemmatize(word));
                }

                foreach (var word in Tokenizer.Tokenize(service.Description).Where(word => !wordSet.Contains(word)))
                {
                    wordSet.Add(Tokenizer.Lemmatizer.Lemmatize(word));
                }

                foreach (var op in service.Operations)
                {
                    foreach (var word in Tokenizer.Tokenize(op.Name).Where(word => !wordSet.Contains(word)))
                    {
                        wordSet.Add(Tokenizer.Lemmatizer.Lemmatize(word));
                    }

                    foreach (var word in Tokenizer.Tokenize(op.Description).Where(word => !wordSet.Contains(word)))
                    {
                        wordSet.Add(Tokenizer.Lemmatizer.Lemmatize(word));
                    }

                    foreach (var word in op.Input.Parameters.SelectMany(param => Tokenizer.Tokenize(param.Name).Where(word => !wordSet.Contains(word))))
                    {
                        wordSet.Add(Tokenizer.Lemmatizer.Lemmatize(word));
                    }

                    foreach (var word in op.Output.Parameters.SelectMany(param => Tokenizer.Tokenize(param.Name).Where(word => !wordSet.Contains(word))))
                    {
                        wordSet.Add(Tokenizer.Lemmatizer.Lemmatize(word));
                    }
                }
            }

            return wordSet.ToList();
        }
    }
 
    /// <summary>
    /// Describes a web service
    /// </summary>
    public class ServiceDefinition
    {
        public string Name { get; set; }
        public string Description { get; set; }
        public List<ServiceOperation> Operations { get; private set; }

        public ServiceDefinition()
        {
            Operations = new List<ServiceOperation>();
        }
    }

    /// <summary>
    /// Describes a service's operations
    /// </summary>
    public class ServiceOperation
    {
        public string Name { get; set; }
        public string Description { get; set; }
        public OpInOut Input { get; set; }
        public OpInOut Output { get; set; }
    }

    /// <summary>
    /// Describes an input or output of an operation
    /// </summary>
    public class OpInOut
    {
        public string Name { get; set; }
        public List<InOutParam> Parameters { get; private set; }

        public OpInOut()
        {
            Parameters = new List<InOutParam>();
        }
    }

    /// <summary>
    /// Describes a parameter of an input or output
    /// </summary>
    public class InOutParam
    {
        public string Name { get; set; }
        public string Type { get; set; }
    }
}
